#include "DisplayCharacter.h"
#include <time.h>

DisplayCharacter::DisplayCharacter() {
   current = 0;
   lastPlaying = 0;
   lastTime = 0;
}

DisplayCharacter::~DisplayCharacter() {
}

void DisplayCharacter::OnRender(const aiScene* character) {
  current += clock() / double(CLOCKS_PER_SEC) - lastPlaying;
  double time = current;
  const aiAnimation* anim = character->mAnimations[0];
  if(anim && anim->mDuration > 0.0) {
     double tps = anim->mTicksPerSecond ? anim->mTicksPerSecond : 25.0;
     time = fmod(time, anim->mDuration / tps);
  }
  Evaluate(time, character, anim);
  lastPlaying = current;
}

void DisplayCharacter::Evaluate(double ptime, const aiScene* character, const aiAnimation *anim) {
   double ticksPerSecond = anim->mTicksPerSecond != 0.0 ? anim->mTicksPerSecond : 25.0;
   ptime *= ticksPerSecond;

   double t = 0.0f;
   if(anim->mDuration > 0.0)
     t = fmod(ptime, anim->mDuration);

   if(transforms.size() != anim->mNumChannels)
      transforms.resize(anim->mNumChannels);

   if(positions.size() != anim->mNumChannels)
      positions.resize(anim->mNumChannels);

   for(unsigned int i = 0; i < anim->mNumChannels; ++i) {
      const aiNodeAnim* channel = anim->mChannels[i];
      aiNode* target = character->mRootNode->FindNode(channel->mNodeName);

      aiVector3D presentPosition( 0, 0, 0);
      if(channel->mNumPositionKeys > 0) {
         unsigned frame = (unsigned int) (t >= lastTime) ? (unsigned int) positions[i].x : 0;
	 while(frame < (channel->mNumPositionKeys - 1)) {
	    if(t < channel->mPositionKeys[frame + 1].mTime)
	       break;
	    frame++;
	 }
	 unsigned int nextFrame = (frame + 1) % channel->mNumPositionKeys;
	 const aiVectorKey& key = channel->mPositionKeys[frame];
	 const aiVectorKey& nextKey = channel->mPositionKeys[nextFrame];
	 double diffTime = nextKey.mTime - key.mTime;
	 if(diffTime < 0.0) diffTime += anim->mDuration;
	 if(diffTime > 0) {
	    float factor = float((t - key.mTime) / diffTime);
	    presentPosition = key.mValue + (nextKey.mValue - key.mValue) * factor;
	 }
	 else
	    presentPosition = key.mValue;

	 positions[i].x = frame;
      }

      aiQuaternion presentRotation(1, 0, 0, 0);
      if(channel->mNumRotationKeys > 0) {
         unsigned frame = (unsigned int) (t >= lastTime) ? (unsigned int) positions[i].y : 0;
	 while(frame < channel->mNumRotationKeys - 1) {
	    if(t < channel->mRotationKeys[frame + 1].mTime)
	       break;
	    frame++;
	 }

	 unsigned int nextFrame = (frame + 1) % channel->mNumRotationKeys;
	 const aiQuatKey& key = channel->mRotationKeys[frame];
	 const aiQuatKey& nextKey = channel->mRotationKeys[nextFrame];
	 double diffTime = nextKey.mTime - key.mTime;
	 if(diffTime < 0.0) diffTime += anim->mDuration;
	 if(diffTime > 0) {
	    float factor = float((t - key.mTime) / diffTime);
	    aiQuaternion::Interpolate(presentRotation, key.mValue, nextKey.mValue, factor);
	 }
	 else
	    presentRotation = key.mValue;

	 positions[i].y = frame;
       }

      aiVector3D presentScaling(1, 1, 1);
      if(channel->mNumScalingKeys > 0) {
         unsigned int frame = (unsigned int) (t >= lastTime) ? (unsigned int) positions[i].z : 0;
	 while(frame < channel->mNumScalingKeys - 1) {
	    if(t < channel->mScalingKeys[frame + 1].mTime) break;
	    frame++;
	 }
	 presentScaling = channel->mScalingKeys[frame].mValue;
	 positions[i].z = frame;
      }

     aiMatrix4x4& mat = transforms[i];
     mat = aiMatrix4x4(presentRotation.GetMatrix());
     mat.a1 *= presentScaling.x; mat.b1 *= presentScaling.x; mat.c1 *= presentScaling.x;
     mat.a2 *= presentScaling.y; mat.b2 *= presentScaling.y; mat.c2 *= presentScaling.y;
     mat.a3 *= presentScaling.z; mat.b3 *= presentScaling.z; mat.c3 *= presentScaling.z;
     mat.a4 = presentPosition.x;
     mat.b4 = presentPosition.y;
     mat.c4 = presentPosition.z;
     target->mTransformation = mat;
     
   }
   lastTime = t;
}

void DisplayCharacter::DrawBone(const aiScene* character, aiMesh *mesh, std::vector<aiVector3D> *resultPos) {
   std::vector<aiMatrix4x4> boneMatrices(mesh->mNumBones);
   for(unsigned a = 0; a < mesh->mNumBones; ++a) {
      const aiBone* bone = mesh->mBones[a];
      aiNode* node = character->mRootNode->FindNode(bone->mName.data);

      boneMatrices[a] = bone->mOffsetMatrix;
      const aiNode* tempNode = node;
      while(tempNode) {
         boneMatrices[a] = tempNode->mTransformation * boneMatrices[a];
	 tempNode = tempNode->mParent;
      }
   }
   
   resultPos->clear();
   resultPos->resize(mesh->mNumVertices * 3);
   std::vector<aiVector3D> Pos(mesh->mNumVertices);
   aiNode* hipNode = character->mRootNode->FindNode("hips");
   aiMatrix4x4 hipM = hipNode->mTransformation;
   aiMatrix4x4::Translation(aiVector3D(-hipM.a4, 0, -hipM.c4), hipM);
   for(unsigned a = 0; a < mesh->mNumBones; ++a) {
      const aiBone* bone = mesh->mBones[a];
      const aiMatrix4x4& trans = boneMatrices[a];

      for(unsigned b = 0; b <  bone->mNumWeights; ++b) {
         const aiVertexWeight& weight = bone->mWeights[b];
	 unsigned vertexID = weight.mVertexId;
	 const aiVector3D& pos = mesh->mVertices[vertexID];
	 resultPos->at(vertexID) = resultPos->at(vertexID) + weight.mWeight * (hipM * trans * pos);
      }
   }
}
